<!DOCTYPE html>
<html>

<head>
    <style>
        body {
            display: flex;
            justify-content: center;
            align-items: center;
            flex-direction: column;
            width: 100%;
            height: 100%;
            margin: 0;
            padding: 0;
            background: url('./images/bg.png');
            background-position: center 0;
            background-size: 100% 100%;
            background-repeat: no-repeat;
            position: relative;
        }

        .title {
            font-size: 3.6vw;
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            color: #fff;
            display: none;
            flex-direction: column;
            align-items: center;
        }

        .title>.sub-title {
            font-size: 1.1vw;
            margin-top: 1.1vw;
            padding: 8px 0;
            border-top: 1px solid #fff;
            border-bottom: 1px solid #fff;
            opacity: 0.8;
        }

        .but {
            position: fixed;
            top: 10px;
            left: 100px;
        }

        #three {
            width: 100vw;
            height: 100vh;
            overflow: hidden;
        }

        #particleOcean {
            width: 100vw;
            height: 280px;
            overflow: hidden;
            /* border: 1px solid #000; */
            position: fixed;
            bottom: 0px;
            /* background: #000; */
        }
    </style>
</head>

<body>
    <div id='three'></div>
    <div class="but">
        <button onclick="showText()">文本</button>
        <button onclick="setBG()">背景</button>
    </div>
    <div class="title">
        <div class="main-title">科技赋能 让数智农文旅更精彩</div>
        <div class="sub-title">国内领先的数智文旅AI+数据运营服务商</div>
    </div>
    <div id='particleOcean'></div>

</body>
<script src="./js/three.js"></script>
<script src="./js/rotationControls.js"></script>
<script type="text/javascript" src="./js/stats.min.js"></script>
<script type="text/javascript" src="./js/hexagonData.js"></script>
<script type="text/javascript" src="./js/particleOcean.js"></script>
<script>
    // particleOceanStart() 
    
    let earthDom = document.getElementById("three");

    function onWindowResize() { // 监听屏幕宽高
        camera.aspect = earthDom.clientWidth / earthDom.clientHeight;
        camera.updateProjectionMatrix();
        renderer.setSize(earthDom.clientWidth, earthDom.clientHeight);
    }

    function setBG() {
        document.getElementsByTagName('body')[0].style.background = "#" + ("00000" + ((Math.random() * 16777215 + 0.5) >> 0).toString(16)).slice(-6);
    }

    let elShow = false
    function showText() {
        elShow = !elShow
        document.getElementsByClassName('title')[0].style.display = elShow ? 'flex' : 'none'
    }

    const CITY_RADIUS = 100,
        CITY_MARGIN = 1,
        BLINT_SPEED = 0.05,
        HEXAGON_RADIUS = 3,
        radius = 100
    let scene, camera, renderer, clock

    let particleBoxList = []

    let earthBox = new THREE.Group() // 地球模组：将会包含地球大陆粒子、云层、六边形光圈
    let isAutomaticRotation = true // 地球模组默认自动旋转

    let earthImg, earthParticles = new THREE.Object3D(),
        cloud = new THREE.Object3D(),
        hexagon = new THREE.Object3D(),
        dotTexture = new THREE.TextureLoader().load('./images/dot.png'),
        hexagonColor = ['#55e1ee', '#55e1ee']
    // earthParticles.name = 'earthParticles'

    let satellites = [] // 存放所有创建的精灵

    /**  
   * 返回一个卫星和轨道的组合体  
   * @param satelliteSize 卫星的大小  
   * @param satelliteRadius 卫星的旋转半径  
   * @param rotation 组合体的x,y,z三个方向的旋转角度  
   * @param speedY 卫星y轴运动速度  
   * @param speedZ 卫星z轴运动速度  
   * @param scene 场景  
   * @returns {{satellite: THREE.Mesh, speed: *}} 卫星组合对象;速度  
   */
    let bezier = []
    const TIME = 1000 * 6// 入场动效持续时间


    const initSatellite = (satelliteSize, satelliteRadius, rotation, speedY, speedZ) => {
        // let track = new THREE.Mesh(new THREE.RingGeometry(satelliteRadius, satelliteRadius + 0.15, 50, 1), new THREE.MeshBasicMaterial()); // 轨道

        //网格模型对象Mesh 、球体SphereGeometry
        let centerMesh = new THREE.Mesh(new THREE.SphereGeometry(10000, 0, 0), new THREE.MeshLambertMaterial()); //材质设定  
        let satellite = new THREE.Sprite(new THREE.SpriteMaterial({ // 精灵材质
            map: new THREE.CanvasTexture(sprite),
            blending: THREE.AdditiveBlending
        }));
        satellite.scale.x = satellite.scale.y = satellite.scale.z = satelliteSize;
        satellite.position.set(satelliteRadius, 0, 0);

        let pivotPoint = new THREE.Object3D();

        pivotPoint.add(satellite);
        // pivotPoint.add(track); // 添加轨道
        centerMesh.add(pivotPoint);

        centerMesh.rotation.set(rotation.x, rotation.y, rotation.z);
        // 设置粒子轨道的坐标并保存,入场动画使用
        centerMesh.position.set(Math.random() * 200 - 100, Math.random() * 400 - 200, Math.random() * 2000)
        const { x, y, z } = centerMesh.position
        // initLiziBoxPosition.push({ x,y, z,})
        // console.log(initLiziBoxPosition);
        scene.add(centerMesh);
        particleBoxList.push(centerMesh)

        // 三次贝塞尔曲线轨迹，粒子在某个时间内遵循该轨迹对应的坐标
        // 创建v0，v3起始点，v2，v3中间控制点
        let v0 = new THREE.Vector3(x, y, z)
        let v1 = new THREE.Vector3(x * 0.8, y * 0.8, z * 0.8)
        let v2 = new THREE.Vector3(x * 0.0001, y * 0.0001, z * 0.0001)
        let v3 = new THREE.Vector3(0, 0, 0)
        let curve = new THREE.CubicBezierCurve3(v0, v1, v2, v3);
        let points = curve.getPoints(TIME); // 总点位序列数匹配时间(毫秒数)
        bezier.push(points)

        return { satellite: centerMesh, speedY, speedZ };
    };
    /**
     * 实现发光星星  
     * @param color 颜色的r,g和b值,比如：“123,123,123”;  
     * @returns {Element} 返回canvas对象  
     */
    const generateSprite = (color) => {
        let canvas = document.createElement('canvas');
        canvas.width = 16;
        canvas.height = 16;
        let context = canvas.getContext('2d');
        let gradient = context.createRadialGradient(canvas.width / 2, canvas.height / 2, 0, canvas.width / 2, canvas.height / 2, canvas.width / 2);
        gradient.addColorStop(0, 'rgba(' + color + ',1)');
        gradient.addColorStop(0.2, 'rgba(' + color + ',1)');
        gradient.addColorStop(0.4, 'rgba(' + color + ',.6)');
        gradient.addColorStop(1, 'rgba(0,0,0,0)');
        context.fillStyle = gradient;
        context.fillRect(0, 0, canvas.width, canvas.height);
        return canvas;
    };
    let sprite = generateSprite('0,233,255') // 存储精灵材质





    main()
    let earthImgData
    function main() {
        earthImg = document.createElement('img')
        earthImg.src = './images/world.png'
        earthImg.onload = () => {
            let earthCanvas = document.createElement('canvas')
            let earthCtx = earthCanvas.getContext('2d')
            earthCanvas.width = earthImg.width
            earthCanvas.height = earthImg.height
            earthCtx.drawImage(earthImg, 0, 0, earthImg.width, earthImg.height)
            earthImgData = earthCtx.getImageData(0, 0, earthImg.width, earthImg.height)
            // basic scene 创建场景
            createBasicScene()
            // 光锥
            createObjects()
            // 球面打点
            createEarthParticles()
            // 云层
            createCloudGrid()
            // 初始化帧数插件
            initStats()
            // 球面粒子闪烁
            animate()
            // 监听宽高变化
            onWindowResize()

            window.addEventListener('resize', onWindowResize, false);


        }
    }
    let arrive = true
    let timer = true
    let startTime
    let endPosition = true // 表示是否为最后的位置,为false表示该调用正式render了
    // let initLiziBoxPosition = []


    //初始化性能插件
    let stats;

    function initStats() {
        stats = new Stats();
        document.body.appendChild(stats.dom);
    }

    function render() {

        // let delta = clock.getDelta()
        stats.update();

        requestAnimationFrame(render)

        if (isAutomaticRotation) {
            earthBox.rotation.y -= 0.005
            earthBox.rotation.z -= 0.001
        }

        for (let i = 0; i < satellites.length; i++) { // 精灵
            satellites[i].satellite.rotation.y += satellites[i].speedY;
            satellites[i].satellite.rotation.z += satellites[i].speedZ;
        }

        // for (let i = 0; i < bubbleInfo.length; i++) {
        //     BubbleScale += 0.005
        //     let pos = bubbleInfo[i].scale + (2 * Math.sin(BubbleScale))
        //     bubbleBox.children[i].scale.set(pos, pos, pos)
        //     bubbleTextBox.children[i].scale.set(pos, pos, pos)
        // }
        renderer.render(scene, camera)
    }

    let BubbleScale = 0

    // 初始化实现动效的render
    function renderInit() {
        stats.update();

        if (isAutomaticRotation) {
            earthBox.rotation.y -= 0.005
            earthBox.rotation.z -= 0.001
        }
        if (arrive) { // 设定准备在下一次render时初始化精灵以作入场动效
            arrive = false
            startTime = new Date().getTime();
            setTimeout(() => timer = false, TIME);
        }


        // for (let i = 0; i < bubbleInfo.length; i++) { //气泡
        //     BubbleScale += 0.005
        //     let pos = bubbleInfo[i].scale + (2 * Math.sin(BubbleScale))
        //     bubbleBox.children[i].scale.set(pos, pos, pos)
        //     bubbleTextBox.children[i].scale.set(pos, pos, pos)
        // }

        for (let i = 0; i < satellites.length; i++) { // 精灵
            if (!timer) {  // 设置正常轨迹并调用正式render
                particleBoxList[i].position.set(0, 0, 0)
                earthBox.scale.set(1, 1, 1);
                earthBox.position.set(0, 0, 0)
                particleBoxList[i].children[0].children[0].material.opacity = 1

                if (i === satellites.length - 1) {
                    bezier = null
                    endPosition = false
                    render()
                }
            } else { // 初始化->粒子运动到原本轨迹
                let remaining = (TIME - (new Date().getTime() - startTime)) // 剩余时间
                remaining = remaining > 0 ? remaining : 0

                // 初始化坐标值除以总时间乘以剩余时间得到该时间段内应到达的坐标值
                // 粒子位置更新   匀速
                // let x = (initLiziBoxPosition[i].x / TIME) * remaining
                // let y = (initLiziBoxPosition[i].y / TIME) * remaining
                // let z = (initLiziBoxPosition[i].z / TIME) * remaining
                // particleBoxList[i].position.set(x, y, z)

                // 粒子位置更新   贝塞尔曲线轨迹
                // console.log(TIME - remaining);
                const { x, y, z } = bezier[i][TIME - remaining]
                particleBoxList[i].position.set(x, y, z)

                // 摄像机根据几何体的位置进行投影(视角中心始终对其该几何体)
                // camera.lookAt(particleBoxList[i].position)  


                // 地球大小更新
                let eart = 1 - (0.15 / TIME * remaining)
                earthBox.scale.set(eart, eart, eart);

                // 地球位置更新
                // let eartPositiony = (-100 / TIME * remaining)
                // let eartPositionz =  (-100 / TIME * remaining)
                // earthBox.position.set(0, eartPositiony, 0);




                // 粒子透明度更新
                let opacity = 1 - (1 / TIME) * remaining
                particleBoxList[i].children[0].children[0].material.opacity = opacity

                // 地球粒子颜色
                // earthParticles.children[0].material.color = {
                //   b: 0.036,
                //   g:0.0155,
                //   r: 0.037
                // }
                // earthParticles.children[1].material.color = {
                //   b: 0.036,
                //   g:0.0155,
                //   r: 0.037
                // }
            }
        }

        if (endPosition) {
            requestAnimationFrame(renderInit)
            renderer.render(scene, camera)
        }

    }


    // const bubbleInfo = [
    //     {
    //         position: { x: -126, y: 75, z: 110 },
    //         scale: 42,
    //         map: new THREE.CanvasTexture(getTextCanvas('数聚佳'))

    //     },
    //     {
    //         position: { x: -150, y: -20, z: 110 },
    //         scale: 42,
    //         map: new THREE.CanvasTexture(getTextCanvas('景管佳'))

    //     },
    //     {
    //         position: { x: -105, y: -76, z: 110 },
    //         scale: 42,
    //         map: new THREE.CanvasTexture(getTextCanvas('乡管佳'))

    //     },
    //     {
    //         position: { x: -42, y: -105, z: 110 },
    //         scale: 42,
    //         map: new THREE.CanvasTexture(getTextCanvas('力石小知'))

    //     },
    //     {
    //         position: { x: 75, y: -90, z: 110 },
    //         scale: 42,
    //         map: new THREE.CanvasTexture(getTextCanvas('产业佳'))

    //     },
    //     {
    //         position: { x: 133, y: -45, z: 110 },
    //         scale: 42,
    //         map: new THREE.CanvasTexture(getTextCanvas('游管佳'))

    //     },
    //     {
    //         position: { x: 175, y: 25, z: 110 },
    //         scale: 42,
    //         map: new THREE.CanvasTexture(getTextCanvas('票务佳'))

    //     },
    //     {
    //         position: { x: 120, y: 90, z: 110 },
    //         scale: 42,
    //         map: new THREE.CanvasTexture(getTextCanvas('力石PAY'))

    //     },
    // ]

    let bubbleBox = new THREE.Object3D();
    let bubbleTextBox = new THREE.Object3D();


    function createBasicScene() {
        // let dom = document.getElementById("three");
        scene = new THREE.Scene(); // 创建场景
        camera = new THREE.PerspectiveCamera(20, earthDom.clientWidth / earthDom.clientHeight, 0.1, 1040);
        camera.position.set(0, 0, 940);//设置相机位置  
        renderer = new THREE.WebGLRenderer({
            alpha: true, // 背景透明
            antialias: true
        });
        renderer.setSize(earthDom.clientWidth, earthDom.clientHeight);//设置窗口尺寸  
        earthDom.appendChild(renderer.domElement);

        clock = new THREE.Clock() // 时钟

        let earthTrack = new THREE.Group() // 存放随地球旋转的一条精灵串

        // 精灵串
        for (let i = 0; i < 150; i++) {

            // 随机流动的精灵（随机分发xyz轴位置并移动）
            // satellites.push(initSatellite(Math.random() * 4, 2.5 * radius, { x: -Math.PI * Math.random() * 400 - 200, y: Math.PI * Math.random() * 400 - 200, z: Math.random() * 400 - 200 }, 0.0004, 0.001));

            // 围绕地球散落的精灵
            satellites.push(initSatellite(Math.random() * 4, 1.2 * radius, { x: -Math.PI * Math.random() * 0.42, y: Math.PI * 0.15, z: Math.random() * 200 - 100 }, 0.000, 0.005));

            // 固定的轨道精灵
            satellites.push(initSatellite(3.2, 1.5 * radius, { x: -Math.PI * 0.42, y: Math.PI * 0.95, z: i * 0.13 }, 0.0, -0.011));

            // 添加随地球固定旋转的轨道精灵
            let satel = initSatellite(2.2, 1.5 * radius, { x: -Math.PI * 0.22, y: Math.PI * 0.25, z: i * 0.13 }, 0.0, 0.007)

            // 随地球固定旋转的散落的精灵
            // let satelScatter = initSatellite(Math.random() * 4,  (2 + Math.random()) * radius, { x: -Math.PI * Math.random() * 0.92, y: Math.PI * 0.15, z: Math.random() * 200 - 100 }, 0.000, 0.005);

            let satelScatter = initSatellite(2, 1.7 * radius, { x: -Math.PI * Math.random() * 0.92, y: Math.PI * 0.15, z: Math.random() * 200 - 100 }, 0.000, 0.005);

            satellites.push(satel);
            satellites.push(satelScatter);
            earthTrack.add(satel.satellite)
            earthTrack.add(satelScatter.satellite)
        }
        earthTrack.name = 'earthTrack'
        earthBox.add(earthTrack)

        // const bubbleMap = new THREE.SpriteMaterial({ // 气泡材质
        //     map: new THREE.TextureLoader().load('./images/circle.png'),
        //     blending: THREE.AdditiveBlending
        // })

        // for (let i = 0; i < bubbleInfo.length; i++) {
        //     let bubble = new THREE.Sprite(bubbleMap);
        //     const { x, y, z } = bubbleInfo[i].position
        //     const scale = bubbleInfo[i].scale
        //     bubble.position.set(x, y, z);
        //     // bubble.scale.set(0, 0, 0);
        //     bubble.scale.set(scale, scale, scale);
        //     bubble.name = i
        //     bubbleBox.add(bubble)

        // }
        // scene.add(bubbleBox)

        // for (let i = 0; i < bubbleInfo.length; i++) {
        //     const bubbleMapText = new THREE.SpriteMaterial({ // 气泡文字
        //         map: bubbleInfo[i].map,
        //         blending: THREE.AdditiveBlending
        //     })
        //     let bubbleText = new THREE.Sprite(bubbleMapText);
        //     const { x, y, z } = bubbleInfo[i].position
        //     const scale = bubbleInfo[i].scale
        //     bubbleText.position.set(x, y, z);
        //     // bubbleText.scale.set(0, 0, 0);
        //     bubbleText.scale.set(scale, scale, scale);
        //     bubbleText.name = 'text' + i
        //     bubbleTextBox.add(bubbleText)

        // }
        // scene.add(bubbleTextBox)
    }

    // function makeTextSprite (message, i, parameters) {
    //         if (parameters === undefined) parameters = {}
    //         var fontface = parameters.hasOwnProperty("fontface") ? parameters["fontface"] : "Arial"
    //         /* 字体大小 */
    //         var fontsize = parameters.hasOwnProperty("fontsize") ? parameters["fontsize"] : 30
    //         /* 边框厚度 */
    //         var borderThickness = parameters.hasOwnProperty("borderThickness") ? parameters["borderThickness"] : 30
    //         /* 边框颜色 */
    //         var borderColor = parameters.hasOwnProperty("borderColor") ? parameters["borderColor"] : {
    //             r: 0,
    //             g: 0,
    //             b: 0,
    //             a: 1.0
    //         }
    //         /* 背景颜色 */
    //         var backgroundColor = parameters.hasOwnProperty("backgroundColor") ? parameters["backgroundColor"] : {
    //             r: 255,
    //             g: 255,
    //             b: 255,
    //             a: 1.0
    //         }

    //         let bgImg = document.createElement('img')
    //         bgImg.src = './images/circle.png'
    //         /* 创建画布 */
    //         var canvas = document.createElement('canvas')
    //         var context = canvas.getContext('2d')
    //         bgImg.onload = () => {
    //             context.drawImage(bgImg, 0, 0, 200, 100)
    //         }
    //         /* 字体加粗 */
    //         context.font = "Bold " + fontsize + "px " + fontface
    //         /* 获取文字的大小数据，高度取决于文字的大小 */
    //         var metrics = context.measureText(message)
    //         var textWidth = 100
    //         /* 背景颜色 */
    //         // context.fillStyle = "rgba(" + backgroundColor.r + "," + backgroundColor.g + "," + backgroundColor.b + "," +
    //         //     backgroundColor.a + ")"
    //         /* 边框的颜色 */
    //         // context.strokeStyle = "rgba(" + borderColor.r + "," + borderColor.g + "," + borderColor.b + "," + borderColor
    //         //     .a + ")"
    //         // context.lineWidth = borderThickness
    //         /* 字体颜色 */
    //         context.fillStyle = "rgba(255, 255, 255, 1.0)"
    //         context.fillText(message, borderThickness, fontsize + borderThickness)
    //         /* 画布内容用于纹理贴图 */

    //         var texture = new THREE.Texture(canvas)
    //         texture.needsUpdate = true

    //         var spriteMaterial = new THREE.SpriteMaterial({
    //             map: texture
    //         })
    //         var sprite = new THREE.Sprite(spriteMaterial)
    //         /* 缩放比例 */
    //         // sprite.scale.set(10, 5, 0)
    //         // console.log(sprite);

    //         const { x, y, z } = bubbleInfo[i].position
    //         const scale = bubbleInfo[i].scale
    //         sprite.position.set(x, y, z);
    //         // sprite.scale.set(0, 0, 0);
    //         sprite.scale.set(scale, scale, scale);
    //         sprite.name = i
    //         // return sprite
    //         bubbleBox.add(sprite)
    //     }



    function getTextCanvas(text) {
        let width = 256, height = 256;
        let canvas = document.createElement('canvas');
        canvas.width = width;
        canvas.height = height;
        let ctx = canvas.getContext('2d');
        // ctx.fillStyle = '#C3C3C3';
        // ctx.fillStyle = '#FFFFFF';
        // ctx.fillRect(0, 0, width, height);
        ctx.font = 50 + 'px " Arial';
        ctx.fillStyle = '#FFFFFF';
        ctx.textAlign = 'center';
        ctx.textBaseline = 'middle';
        ctx.fillText(text, width / 2, height / 2);
        return canvas;
    }

    // 地标
    function createObjects() {
        for (let i = 0, length = countries.length; i < length; i++) {
            const position = createPosition(countries[i].position)
            const index = Math.floor(Math.random() * 2)
            createHexagon(position, index) // 地标
        }
    }

    // 云层
    function createCloudGrid() {
        THREE.XRayMaterial = function (options) {
            let uniforms = {
                uTex: {
                    type: "t",
                    value: options.map || new THREE.Texture
                },
                offsetRepeat: {
                    value: new THREE.Vector4(0, 0, 1, 1)
                },
                alphaProportion: {
                    type: "1f",
                    value: options.alphaProportion || .5
                },
                diffuse: {
                    value: options.color || new THREE.Color(16777215)
                },
                opacity: {
                    value: options.opacity || 1
                },
                gridOffset: {
                    value: 0
                }
            }
            // 着色器
            return new THREE.ShaderMaterial({
                uniforms: uniforms,
                vertexShader: ` 
                    varying float _alpha;
                    varying vec2 vUv;
                    uniform vec4 offsetRepeat;
                    uniform float alphaProportion;
                    void main() {
                    gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
                    vUv = uv * offsetRepeat.zw + offsetRepeat.xy;
                    vec4 worldPosition = modelMatrix * vec4( vec3( position ), 1.0 );
                    vec3 cameraToVertex = normalize( cameraPosition - worldPosition.xyz);
                    _alpha = 1.0 - max( 0.0, dot( normal, cameraToVertex ) );
                    _alpha = max( 0.0, (_alpha - alphaProportion) / (1.0 - alphaProportion) );
                    }`,

                fragmentShader: `
                    uniform sampler2D uTex;
                    uniform vec3 diffuse;
                    uniform float opacity;
                    uniform float gridOffset;
                    varying float _alpha;
                    varying vec2 vUv;
                    void main() {
                    vec4 texColor = texture2D( uTex, vUv );
                    float _a = _alpha * opacity;
                    if( _a <= 0.0 ) discard;
                    _a = _a * ( sin( vUv.y * 2000.0 + gridOffset ) * .2 + .2 );
                    gl_FragColor = vec4( texColor.rgb * diffuse, _a );
                    }`,
                transparent: !0,
                blending: THREE.AdditiveBlending,
                depthTest: !1
            })
        }
        // SphereGeometry 参数 半径1倍的地球大小，经纬度两个方向
        let geometry = new THREE.SphereGeometry(1 * radius, 66, 44),
            map = new THREE.TextureLoader().load('./images/cloud-min.png')
        map.wrapT = THREE.ClampToEdgeWrapping
        map.wrapS = THREE.ClampToEdgeWrapping
        let material = new THREE.XRayMaterial({
            map: map,
            alphaProportion: .25,
            color: new THREE.Color(263385797),
            opacity: 0,
            gridOffsetSpeed: .6
        }),
            mesh = new THREE.Mesh(geometry, material)
        mesh.matrixAutoUpdate = !1
        cloud.add(mesh)
        earthBox.add(cloud)
        cloud.name = 'cloud'
        // scene.add(earthBox)
    }

    // 绘制六边形
    function createHexagon(position, index) {
        const color = hexagonColor[index]
        let hexagonLine = new THREE.CircleGeometry(HEXAGON_RADIUS, 6) // 六边形
        let hexagonPlane = new THREE.CircleGeometry(HEXAGON_RADIUS - CITY_MARGIN, 6)
        let vertices = hexagonLine.vertices
        vertices.shift() // 第一个节点是中心点
        let circleLineGeom = new THREE.Geometry()
        circleLineGeom.vertices = vertices
        let materialLine = new THREE.MeshBasicMaterial({
            color: color,
            side: THREE.DoubleSide,
        })
        let materialPlane = new THREE.MeshBasicMaterial({
            color: color,
            side: THREE.DoubleSide,
            opacity: 0.5
        })
        let circleLine = new THREE.LineLoop(circleLineGeom, materialLine)
        let circlePlane = new THREE.Mesh(hexagonPlane, materialPlane)
        circleLine.position.copy(position)
        circlePlane.position.copy(position)
        circlePlane.lookAt(new THREE.Vector3(0, 0, 0))
        circleLine.lookAt(new THREE.Vector3(0, 0, 0))

        hexagon.add(circleLine)
        // hexagon.add(circlePlane) // 实心
        earthBox.add(hexagon)
        hexagon.name = 'hexagon'
        // scene.add(earthBox)
    }

    // 画粒子地球
    function createEarthParticles() {
        let positions = []
        let materials = []
        let sizes = []
        for (var i = 0; i < 2; i++) {
            positions[i] = { positions: [] }
            sizes[i] = { sizes: [] }
            let mat = new THREE.PointsMaterial()
            mat.size = 8 // 粒子大小
            mat.color = new THREE.Color(hexagonColor[0])
            mat.map = dotTexture
            mat.depthWrite = false
            mat.transparent = true
            mat.opacity = 0
            mat.side = THREE.FrontSide
            mat.blending = THREE.AdditiveBlending
            let n = i / 2
            mat.t_ = n * Math.PI * 2
            mat.speed_ = BLINT_SPEED
            mat.min_ = .2 * Math.random() + .5
            mat.delta_ = .1 * Math.random() + .1
            mat.opacity_coef_ = 1
            materials.push(mat)
        }
        let spherical = new THREE.Spherical
        spherical.radius = radius
        const step = 250
        for (let i = 0; i < step; i++) {
            let vec = new THREE.Vector3
            let radians = step * (1 - Math.sin(i / step * Math.PI)) / step + .5 // 每个纬线圈内的角度均分
            for (let j = 0; j < step; j += radians) {
                let c = j / step, // 底图上的横向百分比
                    f = i / step, // 底图上的纵向百分比
                    index = Math.floor(2 * Math.random())
                let pos = positions[index]
                let size = sizes[index]
                if (isLandByUV(c, f)) { // 根据横纵百分比判断在底图中的像素值
                    spherical.theta = c * Math.PI * 2 - Math.PI / 2 // 横纵百分比转换为theta和phi夹角
                    spherical.phi = f * Math.PI // 横纵百分比转换为theta和phi夹角
                    vec.setFromSpherical(spherical) // 夹角转换为世界坐标
                    pos.positions.push(vec.x)
                    pos.positions.push(vec.y)
                    pos.positions.push(vec.z)
                    if (j % 3 === 0) size.sizes.push(6.0)
                }
            }
        }
        for (let i = 0; i < positions.length; i++) {
            let pos = positions[i],
                size = sizes[i],
                bufferGeom = new THREE.BufferGeometry,
                typedArr1 = new Float32Array(pos.positions.length),
                typedArr2 = new Float32Array(size.sizes.length)
            for (let j = 0; j < pos.positions.length; j++) {
                typedArr1[j] = pos.positions[j]
            }
            for (let j = 0; j < size.sizes.length; j++) {
                typedArr2[j] = size.sizes[j]
            }
            bufferGeom.addAttribute("position", new THREE.BufferAttribute(typedArr1, 3))
            bufferGeom.addAttribute('size', new THREE.BufferAttribute(typedArr2, 1))
            bufferGeom.computeBoundingSphere()
            let particle = new THREE.Points(bufferGeom, materials[i])
            earthParticles.add(particle)
        }
        earthParticles.name = 'earthParticles'
        earthBox.add(earthParticles)
        scene.add(earthBox)
        // console.log(earthBox);

    }

    // 球面粒子闪烁 持续循环
    function animate() {
        // requestAnimationFrame(animate)
        let objects = earthParticles.children
        objects.forEach(obj => {
            let material = obj.material
            material.t_ += material.speed_
            material.opacity = (Math.sin(material.t_) * material.delta_ + material.min_) * material.opacity_coef_
            material.needsUpdate = true
        })
        renderInit() // 执行渲染
    }

    function createPosition(lnglat) {
        let spherical = new THREE.Spherical
        spherical.radius = radius
        const lng = lnglat[0]
        const lat = lnglat[1]
        // const phi = (180 - lng) * (Math.PI / 180)
        // const theta = (90 + lat) * (Math.PI / 180)
        const theta = (lng + 90) * (Math.PI / 180)
        const phi = (90 - lat) * (Math.PI / 180)
        spherical.phi = phi
        spherical.theta = theta
        let position = new THREE.Vector3()
        position.setFromSpherical(spherical)
        return position
    }

    function isLandByUV(c, f) {

        if (!earthImgData) console.error('data error!') // 底图数据

        let n = parseInt(earthImg.width * c) // 根据横纵百分比计算图象坐标系中的坐标
        o = parseInt(earthImg.height * f) // 根据横纵百分比计算图象坐标系中的坐标
        return 0 === earthImgData.data[4 * (o * earthImgData.width + n)] // 查找底图中对应像素点的rgba值并判断
    }



    let raycaster = new THREE.Raycaster();

    let mouse = new THREE.Vector2();

    function onMouseEarth(event) {

        //通过鼠标点击的位置计算出raycaster所需要的点的位置，以屏幕中心为原点，值的范围为-1到1.

        mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
        mouse.y = - (event.clientY / window.innerHeight) * 2 + 1;

        // 通过鼠标点的位置和当前相机的矩阵计算出raycaster
        raycaster.setFromCamera(mouse, camera);

        // 获取raycaster直线和所有模型相交的数组集合
        // let intersects = raycaster.intersectObjects(scene.children);

        // 这里获取的是云层的数据 云层覆盖地球所以能完整监控到地球
        let intersects = raycaster.intersectObjects(earthBox.children[3].children);
        // console.log(intersects);


        let intersectsButton = raycaster.intersectObjects(bubbleBox.children);
        // console.log(intersectsButton[0].object.name);

        //将所有的相交的模型的颜色设置为红色，如果只需要将第一个触发事件，那就数组的第一个模型改变颜色即可
        // for (let i = 0; i < intersects.length; i++) {

        //     intersects[i].object.material.color.set(0xff0000);

        // }

        // 控制地球的旋转
        if (intersects.length > 0) onMouseDown(event)
        if (intersectsButton.length > 0) console.log(intersectsButton[0].object.name);
    }

    earthDom.addEventListener('mousedown', onMouseEarth, false);
    earthDom.addEventListener('mouseup', onMouseup, false);
    earthDom.addEventListener('mouseleave', onMouseup, false);

</script>

</html>